[attr.*]、[class.*])事件綁定:在模板上寫 (事件名)="TS方法()"
<button (click)="doSomething()">點我</button>
當按鈕被點擊,Angular 會呼叫元件類別(.ts)的 doSomething()。
屬性 / 類別綁定:用中括號把 HTML 屬性綁到變數
<a [href]="linkUrl">連結</a>
<button [class.active]="isActive">按鈕</button>
<div [attr.aria-expanded]="isOpen">...</div>
結構指令(回顧):ngIf / ngFor 控制 DOM 是否存在與如何重複渲染。
about.component.tsimport { Component } from '@angular/core';
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: ['./about.component.scss']
})
export class AboutComponent {
isMoreOpen = false; // 控制展開/收起
toggleMore() {
this.isMoreOpen = !this.isMoreOpen;
}
}
about.component.html把原先 hidden 與原生 JS 移除,改用 *ngIf 與屬性綁定:
<section id="about" class="container section" aria-labelledby="about-title">
<h2 id="about-title">關於我</h2>
<p>
我是一名前端工程師,喜歡理解使用者需求並把它落地成產品。近期專注於
Angular、TypeScript、前端架構與效能最佳化。
</p>
<blockquote class="quote">「持續學習,讓自己比昨天更強。」</blockquote>
<!-- 只有在 isMoreOpen 為 true 時才渲染 -->
<p *ngIf="isMoreOpen" id="more-info">
曾參與金融科技與電商專案,也投入設計系統與可存取性。閒暇時間喜歡健身、魔術與寫作分享。
</p>
<!-- 事件綁定:點擊時呼叫 toggleMore() -->
<buttonclass="btn small"
type="button"
(click)="toggleMore()"
[attr.aria-expanded]="isMoreOpen"
[attr.aria-controls]="'more-info'">
{{ isMoreOpen ? '收起介紹' : '更多介紹' }}
</button>
</section>
說明:
ngIf="isMoreOpen"讓段落在狀態為 true 時才存在於 DOM。aria-expanded/aria-controls由狀態同步,對可存取性更友善。- 按鈕文字用插值切換(
{{ ... ? '收起' : '更多' }})。
aria-selected="true"、CSS 的 class 切換)skills.component.ts沿用 Day 9 的資料,加入「目前分類」與「過濾後清單」的 getter。
import { Component } from '@angular/core';
type Category = 'all' | 'frontend' | 'backend' | 'tools';
interface Skill {
name: string;
category: Exclude<Category, 'all'>; // skill 本身不會是 all
}
@Component({
selector: 'app-skills',
templateUrl: './skills.component.html',
styleUrls: ['./skills.component.scss']
})
export class SkillsComponent {
current: Category = 'all';
skills: Skill[] = [
{ name: 'HTML / CSS / SCSS', category: 'frontend' },
{ name: 'TypeScript', category: 'frontend' },
{ name: 'Angular / React / Vue', category: 'frontend' },
{ name: 'Node.js / Express', category: 'backend' },
{ name: 'Git / GitHub / Docker', category: 'tools' },
{ name: 'Vite / Webpack', category: 'tools' }
];
setFilter(cat: Category) {
this.current = cat;
}
// 由目前分類產生要顯示的清單(模板可直接用)
get filtered(): Skill[] {
if (this.current === 'all') return this.skills;
return this.skills.filter(s => s.category === this.current);
}
// ngFor trackBy:提升效能、避免重繪
trackByName(_i: number, s: Skill) { return s.name; }
}
skills.component.html用 (click) + [attr.aria-selected] 切換狀態,列表用 *ngFor 渲染 filtered。
<section id="skills" class="container section" aria-labelledby="skills-title">
<div class="section-header">
<h2 id="skills-title">技能 Skillset</h2>
<!-- 分類按鈕列 -->
<div role="tablist" aria-label="技能分類" class="filters">
<buttonrole="tab"
class="chip"
(click)="setFilter('all')"
[attr.aria-selected]="current === 'all'">
全部
</button>
<buttonrole="tab"
class="chip"
(click)="setFilter('frontend')"
[attr.aria-selected]="current === 'frontend'">
前端
</button>
<buttonrole="tab"
class="chip"
(click)="setFilter('backend')"
[attr.aria-selected]="current === 'backend'">
後端
</button>
<buttonrole="tab"
class="chip"
(click)="setFilter('tools')"
[attr.aria-selected]="current === 'tools'">
工具
</button>
</div>
</div>
<!-- 清單:用 filtered 取代全部 skills -->
<ul class="skill-grid">
<li *ngFor="let s of filtered; trackBy: trackByName">
{{ s.name }}
</li>
</ul>
</section>
說明:
(click)="setFilter('frontend')":事件綁定,點即切換分類。[attr.aria-selected]="current === 'frontend'":用屬性綁定同步高亮狀態。filteredgetter 讓模板很乾淨;trackBy減少重繪。
(click) + ngIf + 屬性綁定),移除原生 JS 依賴。.ts 管理,維護成本更低。hidden 當邏輯
<p hidden>...</p> 然後用 JS 改 hidden
ngIf / [hidden]:建議 ngIf,元件銷毀/建立更乾淨(click)="toggleMoree()" → console 報 undefined(click)="setFilter('tools')"
ngFor="let s of skills.filter(...)" 每次檢測都跑運算.ts 變成 getter 或預先產生的陣列(如 filtered)<button class="chip active"> 然後用 JS 切 class[class.active]="condition" 或 [attr.aria-selected]="condition",資料驅動視覺ngFor="let item of list"
ngFor="let item of list; trackBy: trackById" 回傳穩定識別值明天我們把 Angular 的 雙向綁定 (Two-way binding) 帶進來,做兩個進階改善:
[(ngModel)] + 管線/過濾邏輯)